package westeel.dataserver.utils;

import com.zaxxer.hikari.HikariDataSource;
import org.apache.tomcat.util.http.fileupload.FileUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.*;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.util.StringUtils;

import java.io.*;
import java.util.*;

/**
 * 自定义 DataSourceFactoryOnStart 启动是根据配置文件创建数据源
 * 执行时机:所有的bean定义信息将要被加载到容器中，Bean实例还没有被初始化。
 */

@Configuration
public class DataSourceFactoryOnStart implements BeanDefinitionRegistryPostProcessor {
    //https://www.cnblogs.com/Sora-L/p/16750136.html
    public final static String APPLICATION_PROPS = "application.properties";
    public final static String APPLICATION_PROPS_XML = "config/application.xml";
    private static final String SYSTEM_DATA_SOURCE_PROPS = "systemDataSource.properties";
    private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
    private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
    public static Properties applicationProperties = new Properties();
    public static synchronized Properties getApplicationProperties() {
        return applicationProperties;
    }

    public static synchronized void setApplicationProperties(Properties applicationProperties) {
        DataSourceFactoryOnStart.applicationProperties = applicationProperties;
    }

    public static Set<String> dataSourceNames = Collections.emptySet();
    private static final Logger logger = LoggerFactory.getLogger(DataSourceFactoryOnStart.class);
    public static void InitFromProperties(){
        try {
            FileUtils.forceMkdir(new File("config"));
            File xmlFile = new File(APPLICATION_PROPS_XML);
            Properties properties = new Properties();
            if(!xmlFile.exists()) {
                properties = PropertiesLoaderUtils.loadProperties(new ClassPathResource(APPLICATION_PROPS));
                DataSourceUtils.keepDataSourceInfo(properties);
                SavePropAsXml(properties,APPLICATION_PROPS_XML);
            } else {
                try (InputStream inputStream = new FileInputStream(APPLICATION_PROPS_XML)) {
                    properties.loadFromXML(inputStream);
                }
            }
            Properties systemProperties = PropertiesLoaderUtils.loadProperties(new ClassPathResource(SYSTEM_DATA_SOURCE_PROPS));
            properties.putAll(systemProperties);
            dataSourceNames = DataSourceUtils.getDataSourceNames(properties);
            setApplicationProperties(properties);
        }
        catch (IOException ioException)
        {
            ioException.printStackTrace();
        }
    }
    public static void SavePropAsXml(Properties properties,String xmlPath){
        try(OutputStream outputStream=new FileOutputStream(xmlPath)){
            Properties normalizedProperties = new Properties();
            properties.forEach((key,value)->{
                normalizedProperties.put(key.toString(),value.toString());
            });
            normalizedProperties.storeToXML(outputStream,"");
        } catch (IOException e) {
            logger.warn("failed to save "+xmlPath+" xml",e);
        }
    }

    public static synchronized Properties GetApplicationProperties() {
        return applicationProperties;
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
       try {
            int i = 0;
            for(String dataSourceName:dataSourceNames){
                Map<String, Object> propsMap = DataSourceUtils.getPropertiesStartAt(GetApplicationProperties(), DataSourceUtils.SPRING_DATASOURCE_PREX + dataSourceName);
                //如果注入FactoryBean类就是工厂对象

                registerBean(registry, dataSourceName, HikariDataSource.class,propsMap,0 == (i++));
            }
        }catch (Exception e){
            logger.error("load prop error",e);
        }
    }
    private void registerBean(BeanDefinitionRegistry registry, String name, Class<?> beanClass,Map<String,Object> propsMap,boolean primary){
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(beanClass)
                .setPrimary(primary)
                //不要随意设置setAutowireMode容易产生循环依赖
                //.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE)
                .setRole(BeanDefinition.ROLE_INFRASTRUCTURE)
                .setLazyInit(false)
                .getBeanDefinition();
        if(primary){
            DataSourceCreateHelper.setDefaultDataSourceName(name);
        }
        beanDefinition.getPropertyValues().addPropertyValues(propsMap);
        String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(beanDefinition, registry));
        ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(beanDefinition);
        beanDefinition.setScope(scopeMetadata.getScopeName());
        registry.registerBeanDefinition(beanName, beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
    }
}
